﻿using System.Collections.Generic;
using cludg.Infrastructure.DI;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;

namespace cludg.Infrastructure.DI.Unity
{
	using Core.Attributes;

	/// <summary>
	/// A <see cref="InjectionPolicy"/> class that reads and constructs handlers
	/// based on <see cref="HandlerAttribute"/> on the target.
	/// </summary>
	public class UniversalAttributeInjectionPolicy : InjectionPolicy
	{
		private readonly AttributeDrivenPolicyMatchingRule _attributeMatchRule;

		/// <summary>
		/// Constructs a new instance of the <see cref="AttributeDrivenPolicy"/>.
		/// </summary>
		public UniversalAttributeInjectionPolicy()
			: base("Universal attribute Driven Policy")
		{
			_attributeMatchRule = new AttributeDrivenPolicyMatchingRule();
		}

		/// <summary>
		/// Derived classes implement this method to calculate if the policy
		/// will provide any handler to the specified member.
		/// </summary>
		/// <param name="member">Member to check.</param>
		/// <returns>true if policy applies to this member, false if not.</returns>
		protected override bool DoesMatch(MethodImplementationInfo member)
		{
			bool matchesInterface = member.InterfaceMethodInfo != null ? _attributeMatchRule.Matches(member.InterfaceMethodInfo) : false;
			bool matchesImplementation = _attributeMatchRule.Matches(member.ImplementationMethodInfo);

			return matchesInterface | matchesImplementation;
		}

		/// <summary>
		/// Derived classes implement this method to supply the list of handlers for
		/// this specific member.
		/// </summary>
		/// <param name="member">Member to get handlers for.</param>
		/// <param name="container">The <see cref="IUnityContainer"/> to use when creating handlers,
		/// if necessary.</param>
		/// <returns>Enumerable collection of handlers for this method.</returns>
		protected override IEnumerable<ICallHandler> DoGetHandlersFor(MethodImplementationInfo member, IUnityContainer container)
		{
			if (member.InterfaceMethodInfo != null)
			{
				foreach (UniversalHandlerAttribute attr in ReflectionHelper.GetAllAttributes<UniversalHandlerAttribute>(member.InterfaceMethodInfo, true))
				{
					yield return (ICallHandler)container.Resolve(attr.GetHandlerType());
				}
			}

			foreach (UniversalHandlerAttribute attr in ReflectionHelper.GetAllAttributes<UniversalHandlerAttribute>(member.ImplementationMethodInfo, true))
			{
				yield return (ICallHandler)container.Resolve(attr.GetHandlerType());
			}
		}
	}
}
